package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.common.constant.ProductConstants;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.model.base.BaseEntity;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.mapper.SkuInfoMapper;
import com.atguigu.gmall.product.service.SkuAttrValueService;
import com.atguigu.gmall.product.service.SkuImageService;
import com.atguigu.gmall.product.service.SkuInfoService;
import com.atguigu.gmall.product.service.SkuSaleAttrValueService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/***
 * sku信息表service层实现类
 */
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class SkuInfoServiceImpl implements SkuInfoService {
    @Autowired
    private SkuInfoMapper skuInfoMapper;

    @Autowired
    private SkuImageService skuImageService;

    @Autowired
    private SkuAttrValueService skuAttrValueService;

    @Autowired
    private SkuSaleAttrValueService skuSaleAttrValueService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 保存数据
     *
     * @param skuInfo
     */
    @Override
    public void save(SkuInfo skuInfo) {
        //1 新增sku信息表数据
        skuInfo.setIsSale(ProductConstants.SKU_DOWN_SALE);//设置商品销售状态
        int insert = skuInfoMapper.insert(skuInfo);
        if(insert <= 0){
            throw new GmallException("新增sku信息表数据失败",null);
        }

        Long skuId = skuInfo.getId();//获取skuId
        //2 新增sku图片表数据
        skuImageService.save(skuId,skuInfo.getSkuImageList());

        //3 新增sku平台属性值表数据
        skuAttrValueService.save(skuId,skuInfo.getSkuAttrValueList());

        //4 新增sku销售属性值表数据
        skuSaleAttrValueService.save(skuId,skuInfo.getSpuId(),skuInfo.getSkuSaleAttrValueList());

        //5 保存商品成功，生成静态页面

    }

    /**
     * 分页查询
     *
     * @param pageNum
     * @param size
     * @return
     */
    @Override
    public IPage<SkuInfo> listPage(Integer pageNum, Integer size) {
        //创建分页对象
        Page<SkuInfo> page = new Page<>(pageNum,size);

        //2 调用dao层方法
        IPage<SkuInfo> skuInfoIPage = skuInfoMapper.selectPage(page, null);

        //3 返回结果
        return skuInfoIPage;
    }

    /**
     * 商品上架
     *
     * @param skuId
     */
    @Override
    public void upOrDown(Long skuId,Short status) {
        //1 根据skuId查询到单条sku信息数据
        SkuInfo skuInfo = skuInfoMapper.selectOne(new LambdaQueryWrapper<SkuInfo>().eq(BaseEntity::getId, skuId));

        //2 判断
        if(null == skuInfo){
            //没有数据
            throw new GmallException("数据不存在，上架失败！",null);
        }

        //3 上架操作，修改数据库表内容
        skuInfo.setIsSale(status);
        skuInfoMapper.updateById(skuInfo);

        //4 上架成功，将静态页面上传到nginx的映射目录中去

        //5 下架：删除nginx的映射目录中的静态页面

    }

    /**
     * 主键查询
     *
     * @param skuId
     * @return
     */
    @Override
    public SkuInfo getBySkuId(Long skuId) {
        return skuInfoMapper.selectById(skuId);
    }

    /**
     * 优化查询，使用redis缓存
     *
     * @param skuId
     * @return
     */
    @Override
    public SkuInfo getBySkuIdByRedisOrMySql(Long skuId) {
        //先从redis中查询
        SkuInfo redisSkuInfo = (SkuInfo) redisTemplate.opsForValue().get("skuId:" + skuId + ":info");
        if(redisSkuInfo != null){
            //有数据就返回
            return redisSkuInfo;
        }

        //如果redis中没有数据，从数据查询
        //加锁
        RLock lock = redissonClient.getLock("skuId:" + skuId + ":lock");
        try{
            if(lock.tryLock(100,100, TimeUnit.SECONDS)){
                try{
                    //查询之前再确认redis中有没有数据，防止并发查询浪费资源
                    redisSkuInfo = (SkuInfo) redisTemplate.opsForValue().get("skuId:" + skuId + ":info");
                    if(redisSkuInfo != null ){
                        //有数据就返回
                        return redisSkuInfo;
                    }
                    //从数据库中查询数据
                    SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
                    //数据库如果也没有数据
                    if(skuInfo == null || skuInfo.getId() == null){
                        //传一个空对象，放入redis中
                        skuInfo = new SkuInfo();
                        redisTemplate.opsForValue().set("skuId:" + skuId + ":info",skuInfo,300,TimeUnit.SECONDS);
                        return skuInfo;
                    }
                    //数据有数据，放入redis中
                    redisTemplate.opsForValue().set("skuId:" + skuId + ":info",skuInfo,60*60*24,TimeUnit.SECONDS);
                    return skuInfo;
                }catch (Exception e){
                    log.error("查询skuInfo数据时失败，原因是：" + e.getMessage());
                }finally {
                    //释放锁
                    lock.unlock();
                }
            }
        }catch (Exception e){
            log.error("查询skuInfo数据时，加锁失败，错误原因是：" + e.getMessage());
        }
        return null;
    }

    /**
     * 根据skuId查询价格
     *
     * @param skuId
     * @return
     */
    @Override
    public BigDecimal getSkuInfoPriceBySkuId(Long skuId) {
        return skuInfoMapper.selectById(skuId).getPrice();
    }

    /**
     * 订单生成减库存
     *
     * @param decountMap
     * @return
     */
    @Override
    public Boolean decount(Map<Object,Object> decountMap) {
        //遍历
        decountMap.entrySet().stream().forEach(o -> {
            //获取skuId
            String skuId = (String) o.getKey();
            //获取skuNum
            String skuNum = (String) o.getValue();

            //减库存操作
            int result = skuInfoMapper.decountStock(Long.valueOf(skuId),Integer.valueOf(skuNum));
            if(result<=0){
                throw new GmallException("库存不足,扣减失败!",null);
            }
        });

        return true;
    }

    /**
     * 取消订单，回滚商品库存
     *
     * @param skuIdAndSkuNumMap
     * @return
     */
    @Override
    public Boolean rollbackStock(Map<String, Object> skuIdAndSkuNumMap) {
        //1 获取skuId和SkuNum
        //遍历
        skuIdAndSkuNumMap.entrySet().stream().forEach(o->{
            Long skuId = Long.valueOf(o.getKey());
            Integer SkuNum = Integer.valueOf(o.getValue().toString());

            //2 调用dao层方法
            int result = skuInfoMapper.rollbackStock(skuId,SkuNum);

            //3 判断
            if(result<= 0){
                throw new GmallException("库存回滚失败!",null);
            }
        });

        return true;
    }
}
